/**
 * $Id: httpd.c 5 2009-05-06 15:05:44Z hannes@napoleon.bin-laden.biz $
 * This file is the one that handles the HTTP Connections.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>

#include "httpd.h"			/* Header-file 			*/
#include "buf.h"			/* Buffer functions		*/
#include "tracker.h"		/* Tracker functions	*/
#include "cfg.h"			/* Configuration file	*/

pthread_mutex_t mutex1; /* Mutex */ 

/**
 * httpd_start() basicly just binds to the specified port
 * and forks the threads.
 */
void httpd_start()
{
	int bin, list = 0;
	signal (SIGINT, httpd_exit);

	/* Initiate the Pthread structure. */
	pthread_t thread[THREADS];

	pthread_mutex_init(&mutex1, NULL);

	/* Setting up the socket. */
	struct sockaddr_in addr;
	addr.sin_family			= AF_INET;
	addr.sin_addr.s_addr	= inet_addr(SERVER_ADDR);
	addr.sin_port			= htons(SERVER_PORT);

	/* Creating the socket */
	sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 0, 0);

	if (sock < 0) {
		printf("httpd.c unable to create socket.");
		exit(1);
	}

	/* Bind to socket. */
	bin = bind(sock,(struct sockaddr *) &addr, sizeof(addr));
	if (bin < 0) {
		printf("httpd.c unable to bind.\n");
		exit(1);
	}

	/* Listen */
	list = listen(sock,QUEUE_SIZE);
	if (list < 0) {
		printf("httpd.c unable to listen.\n");
		exit(1);
	}
	
	active_threads = 0;

	/* Main loop. */
	while (1) {
		if (active_threads < THREADS) {
			#ifdef DEBUG
				printf("httpd.c forking thread\n");
			#endif
			pthread_create(&thread[active_threads], NULL, httpd_handle, (void *)active_threads);
			active_threads++;
		} else {
			sleep(1);
		}
	
	}
	close(sock);
}

/**
 * httpd_handle() Handles the HTTP requests
 */
void *httpd_handle(void *thrid)
{
	struct http_thread_vars *http_vars[THREADS];

	pthread_mutex_lock (&mutex1);
	/* Thread-id */
	long tid;
	tid = (long)thrid;
	http_vars[tid] = malloc(sizeof(struct http_thread_vars));
	http_vars[tid]->tid = (int)tid;
	http_vars[tid]->reqs = 0;
	pthread_mutex_unlock (&mutex1);

	while (1) {

		/* Cleaning the buffers */
		memset(http_vars[tid]->buf, 0, BUF_SIZE);
		memset(http_vars[tid]->sendbuf, 0, BUF_SIZE);
		memset(http_vars[tid]->params, 0, 3);

		struct sockaddr_in addr;
		int addr_len = sizeof(addr);

		http_vars[tid]->sa = accept(sock, (struct sockaddr*)&addr, &addr_len);
		recv(http_vars[tid]->sa, http_vars[tid]->buf, BUF_SIZE, 0);


		/* Handle GET Requests */
		if (!strncmp(http_vars[tid]->buf, "GET", 3)) {
			
			#ifdef DEBUG
				printf("httpd.c peer-ip %s\n", inet_ntoa(addr.sin_addr));
			#endif

			/* Insert the Peer's ip into the structure. */
			http_vars[tid]->ip = inet_ntoa(addr.sin_addr);

			/* Parse the buffer */
			http_vars[tid]->params[0] = strtok(http_vars[tid]->buf, " ");
		
			for (http_vars[tid]->i = 1; http_vars[tid]->i < 3; http_vars[tid]->i++) {
				http_vars[tid]->params[http_vars[tid]->i] = strtok(NULL, " ");	
			}

			for (http_vars[tid]->i = 0; http_vars[tid]->i < strlen(http_vars[tid]->params[1]); http_vars[tid]->i++) {
				if (http_vars[tid]->params[1][http_vars[tid]->i] == '?' || http_vars[tid]->params[1][http_vars[tid]->i] == '&' || http_vars[tid]->params[1][http_vars[tid]->i] == '=') {
					http_vars[tid]->params[1][http_vars[tid]->i] = ' ';
				}
			}

			if (!strncmp(http_vars[tid]->params[1]+34, "announce", 8)) {
				tracker_handle_req(http_vars[tid]);
			} else if (!strncmp(http_vars[tid]->params[1], "/status", 7)) { /* Will be added in the future. */
				httpd_send_html(http_vars[tid]->sa);
			} else {
				httpd_send_html(http_vars[tid]->sa);
			}

		} else {
			/* Since we dont support any other request than the GET, we'll print out an 501 Msg. */ 
			httpd_disp_err(http_vars[tid]->sa, 501);
		}

		close(http_vars[tid]->sa);
		http_vars[tid]->reqs++;
	}	
	active_threads--;
}

/**
 * httpd_disp_err() is used to display errors to the client.
 */
void httpd_disp_err(int sa, int err)
{
	struct buf *b;
	char header[256];
	char content[256];
	memset(header, 0, 256);
	memset(content, 0, 256);

	#ifdef DEBUG
		printf("Err: %i\n", err);
	#endif

	b = buf_new();
	switch (err) {
		case 404:
			sprintf(header, "HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: %s\r\nContent-Type: text/html\r\n\r\n", SERVER_VERSION);
			buf_append_data(b, header, strlen(header));
			sprintf(content, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n<head><title>404 - Not found</title>\n</head>\n<body><h1>404 - Boxxy not found!</h1>\n<br />\n<br />\n<strong>%s</strong>\n</body></html>\r\n", SERVER_VERSION);
			buf_append_data(b, content, strlen(content));
			
			#ifdef DEBUG
				printf("Content: %s", b->ptr);
			#endif

			write(sa, b->ptr, b->len);
			break;
		
		case 501:
			sprintf(header, "HTTP/1.1 501 Not Implented\r\nConnection: close\r\nServer: %s\r\nContent-Type: text/html\r\n\r\n");
			buf_append_data(b, header, strlen(header));
			sprintf(content, "<html><head><title>501 - Not Implented</title></head><body><h1>Not Implented</h1></body></html>");
			buf_append_data(b, content, strlen(content));
			write(sa, b->ptr, b->len);
			break;
	
		default:
			break;
	}
}

/**
 * httpd_send_html() is used to send html to the client.
 * Notice: Remember \r\n at the end of content.
 */
void httpd_send_html(int sa)
{
	struct buf *b;
	char header[256];
	char content[256];
	memset(header, 0, 256);
	memset(content, 0, 256);

	b = buf_new();

	sprintf(header, "HTTP/1.1 200 OK\r\nConnection: close\r\nServer: %s\r\nContent-Type: text/html\r\n\r\n", SERVER_VERSION);	
	buf_append_data(b, header, strlen(header));
	sprintf(content, "<html><head><title>:E</title></head><body><h1>Hej :)</body></html>\r\n");
	buf_append_data(b, content, strlen(content));
	write(sa, b->ptr, b->len);
}

void httpd_exit()
{
	printf("\nExiting..\n");
	close(sock);
	exit(1);
	return;
}
